package com.xucia.jsponic.data;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.javascript.Context;

import com.xucia.jsponic.datasource.DataSource;
import com.xucia.jsponic.datasource.DataSourceCanHaveOrphans;
import com.xucia.jsponic.remote.ClientConnection;
import com.xucia.jsponic.remote.ClientTransformationPlugin;
import com.xucia.jsponic.remote.JsponSerializer;
import com.xucia.jsponic.security.SecurityHandler;
import com.xucia.jsponic.security.UserSecurity;
import com.xucia.jsponic.util.StrongSoftSyncHashMap;

public class DataSourceManager {
    static Map<String,DataSource> dataSources;
    static DataSource rootSource;
    static final String CONFIGURATION_FILE = "DataSources.json";
    public static Map<ObjectId,Persistable> sourceObjectMap = new StrongSoftSyncHashMap(100);

    static {
    	try {
			dataSources = new HashMap();
			String webInfLocation = GlobalData.getWebInfLocation();
			File dataDirectory = new File(webInfLocation.substring("file:/".length()) + File.separatorChar + "data");
			if (!dataDirectory.exists())
				dataDirectory.mkdir();
			System.err.println("webInfLocation " + webInfLocation);
			String configurationFile= slurp(DataSourceManager.class.getClassLoader().getResourceAsStream(CONFIGURATION_FILE));
			configurationFile = configurationFile.replaceAll("\\$DATA-DIR\\$",webInfLocation + "/data");
			JSONObject config = new JSONObject(configurationFile);
			try {
				UserSecurity.setInstalledSecurity((SecurityHandler) Class.forName(config.getString("security")).newInstance());
				ClientConnection.setJsponSerializer((JsponSerializer) Class.forName(config.getString("jsponSerializer")).newInstance());
				ClientConnection.setClientTransformationPlugin((ClientTransformationPlugin) Class.forName(config.getString("clientTransformationPlugin")).newInstance());
				JSONArray serverScriptsJSON = config.getJSONArray("serverScripts");
				for (int i = 0; i < serverScriptsJSON.length(); i++) {
					String serverScript = serverScriptsJSON.getString(i);
					Context.enter().evaluateString(GlobalData.getGlobalScope(),slurp(new FileInputStream(serverScript)) , serverScript, 0, null);
				}
			} catch (Exception e) {
				throw new RuntimeException(e);
			} 
			JSONArray dataSourceConfigs = config.getJSONArray("sources");
			for (int i = 0; i < dataSourceConfigs.length(); i++) {
				try{
						JSONObject dataSourceElement = dataSourceConfigs.getJSONObject(i);
						String sourceClass = dataSourceElement.getString("sourceClass");
						String nameSpace  = dataSourceElement.getString("nameSpace");
						DataSource source = (DataSource) Class.forName(sourceClass).newInstance();
						source.initParameters(dataSourceElement);
						source.setId(nameSpace);
						dataSources.put(nameSpace,source);
						if (i==0)
							rootSource = source;
					} catch (Exception e) {
						e.printStackTrace();
					}
				
	    	}
		} catch (IOException e) {
			e.printStackTrace();
		}
    	catch (JSONException e) {
    		throw new RuntimeException(e);
    	}
    }
    public static DataSource getRootSource() {
    	return rootSource;
    }
    public static Persistable getRootObject() {
    	return ObjectId.idForObject(rootSource, "0").getTarget();
    }
    public static DataSource getSource(String nameSpace) {
    	if (nameSpace == null)
    		return getRootSource();
    	return dataSources.get(nameSpace);
    }
    public static Persistable getById(String id) {
    	return getById(ObjectId.idForString(id));
    }
    public static Persistable getById(ObjectId id) {
    	return id.getTarget();
    }
    public static void purgeAllOrphans() {
    	for (DataSource source : dataSources.values()) {
    		if (source instanceof DataSourceCanHaveOrphans)
    			((DataSourceCanHaveOrphans)source).purgeOrphans();
    	}
    }
    public static String slurp (InputStream in) throws IOException {
        StringBuffer out = new StringBuffer();
        byte[] b = new byte[4096];
        for (int n; (n = in.read(b)) != -1;) {
            out.append(new String(b, 0, n));
        }
        return out.toString();
    }

}
